{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f2178096",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import gs_quant\n",
    "from gs_quant.session import GsSession, Environment\n",
    "from gs_quant.data import DataMeasure, DataFrequency\n",
    "from gs_quant.markets.securities import SecurityMaster, AssetIdentifier\n",
    "import datetime as dt\n",
    "import pandas as pd\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7b210150-1de2-4faa-9fa4-918104cca874",
   "metadata": {},
   "outputs": [],
   "source": [
    "# setup client_id and client_secret for connection\n",
    "GsSession.use(Environment.PROD, client_id, client_secret, 'read_product_data')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "abe77632-5046-418a-9151-797558b7cf12",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "def get_vol_and_adv(ticker: str, start_date: dt.date, end_date: dt.date):\n",
    "    \"\"\"\n",
    "    Fetch volatility and average daily trading volume for a stock.\n",
    "\n",
    "    :param ticker: Stock ticker (e.g., 'AAPL')\n",
    "    :param start_date: Start date for the data (e.g., dt.date(2023, 1, 1))\n",
    "    :param end_date: End date for the data (e.g., dt.date(2023, 10, 1))\n",
    "    :return: Dictionary with volatility and ADV/ADTV\n",
    "    \"\"\"\n",
    "    # Get the asset using the ticker\n",
    "    asset = SecurityMaster.get_asset(ticker, AssetIdentifier.TICKER)\n",
    "\n",
    "    # Fetch historical close prices\n",
    "    close_prices = asset.get_data_series(\n",
    "        measure=DataMeasure.CLOSE_PRICE,\n",
    "        frequency=DataFrequency.DAILY,\n",
    "        start=start_date,\n",
    "        end=end_date\n",
    "    )\n",
    "\n",
    "    # Calculate returns and volatility\n",
    "    returns = close_prices.pct_change().dropna()\n",
    "    volatility = returns.std()\n",
    "\n",
    "    # Fetch historical trading volume\n",
    "    trading_volume = asset.get_data_series(\n",
    "        measure=DataMeasure.VOLUME,\n",
    "        frequency=DataFrequency.DAILY,\n",
    "        start=start_date,\n",
    "        end=end_date\n",
    "    )\n",
    "\n",
    "    # Calculate average daily trading volume\n",
    "    adv = trading_volume.mean()\n",
    "\n",
    "    return volatility, adv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "5e5497e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def lifetime_risk_tcm_bps( notional,\n",
    "                           ticker,\n",
    "                           date,\n",
    "                           daily_vol = \"Optional\",\n",
    "                           unwind_speed_as_pct_adv = 0.03):\n",
    "    \n",
    "    start_date = date.replace(year=date.year - 1)\n",
    "    \n",
    "    if daily_vol == \"Optional\":\n",
    "        daily_vol, dollar_adv = get_vol_and_adv(ticker, start_date, date)\n",
    "    else:\n",
    "        dollar_adv = get_vol_and_adv(ticker, start_date, date)[1]\n",
    "    days_to_unwind = abs( notional ) / ( unwind_speed_as_pct_adv * dollar_adv )\n",
    "    lifetime_risk = math.sqrt( 1 / 3 ) * notional * daily_vol * math.sqrt( days_to_unwind )\n",
    "    out = ( lifetime_risk / abs( notional ) ) * 10e3\n",
    "    return( out, daily_vol )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "aa9d762d",
   "metadata": {},
   "outputs": [],
   "source": [
    "notional = 100e6\n",
    "# daily_vol = .05\n",
    "# dollar_adv = 100e6\n",
    "ticker = \"AAPL\"\n",
    "date = dt.date(2025, 6, 9)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "abf6f445",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1594.1136536329607, 0.020907195709856222)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lifetime_risk_tcm_bps( notional, ticker, date )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "f25fe349",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0eff250fd2664b018372f26bb55d482d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(HTML(value='<h3>Lifetime Risk Calculator</h3>'), Text(value='1,000,000', description='Notional:…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import ipywidgets as widgets\n",
    "from IPython.display import display, clear_output\n",
    "import pandas as pd\n",
    "import datetime as dt\n",
    "\n",
    "# Custom formatted text input widget\n",
    "class FormattedTextInput:\n",
    "    def __init__(self, description, initial_value=0):\n",
    "        self.text_widget = widgets.Text(\n",
    "            value=f\"{initial_value:,}\",\n",
    "            description=description,\n",
    "            style={'description_width': 'initial'},\n",
    "            layout=widgets.Layout(width='300px')\n",
    "        )\n",
    "        self.text_widget.observe(self.on_text_change, names='value')\n",
    "        self._updating = False\n",
    "\n",
    "    def on_text_change(self, change):\n",
    "        if self._updating:\n",
    "            return\n",
    "\n",
    "        self._updating = True\n",
    "        current = change['new']\n",
    "\n",
    "        # Remove existing commas for processing\n",
    "        clean_value = current.replace(',', '')\n",
    "\n",
    "        try:\n",
    "            # Only format if it's a valid number\n",
    "            num_value = float(clean_value)\n",
    "            formatted = f\"{num_value:,.2f}\"\n",
    "            if formatted != current:\n",
    "                self.text_widget.value = formatted\n",
    "        except ValueError:\n",
    "            pass  # Keep the current value if invalid\n",
    "\n",
    "        self._updating = False\n",
    "\n",
    "    def get_value(self):\n",
    "        \"\"\"Return the numeric value without commas\"\"\"\n",
    "        try:\n",
    "            return float(self.text_widget.value.replace(',', ''))\n",
    "        except ValueError:\n",
    "            return 0.0\n",
    "\n",
    "# Create a class to handle the GUI\n",
    "class LifetimeRiskCalculator:\n",
    "    def __init__(self):\n",
    "        self.output = widgets.Output()\n",
    "\n",
    "        # Create input widgets\n",
    "        self.notional_input = FormattedTextInput('Notional:', 1000000)\n",
    "        self.ticker = widgets.Text(\n",
    "            value=\"AAPL\",\n",
    "            description='Ticker:',\n",
    "            style={'description_width': 'initial'},\n",
    "            layout=widgets.Layout(width='300px')\n",
    "        )\n",
    "        self.date = widgets.DatePicker(\n",
    "            value=dt.date(2025, 6, 10),\n",
    "            description='Date:',\n",
    "            style={'description_width': 'initial'},\n",
    "            layout=widgets.Layout(width='300px')\n",
    "        )\n",
    "        self.daily_volatility = FormattedTextInput('Daily Volatility (Optional):', 0.0)\n",
    "        self.unwind_speed_as_pct_adv = FormattedTextInput('Unwind Speed as Pct Adv:', 0.03)\n",
    "\n",
    "        # Create calculate button\n",
    "        self.calculate_button = widgets.Button(\n",
    "            description='Calculate Risk',\n",
    "            button_style='primary',\n",
    "            layout=widgets.Layout(width='300px')\n",
    "        )\n",
    "        self.calculate_button.on_click(self.on_calculate_clicked)\n",
    "\n",
    "        # Create reset button\n",
    "        self.reset_button = widgets.Button(\n",
    "            description='Reset',\n",
    "            button_style='warning',\n",
    "            layout=widgets.Layout(width='300px')\n",
    "        )\n",
    "        self.reset_button.on_click(self.on_reset_clicked)\n",
    "\n",
    "        # Display everything\n",
    "        self.container = widgets.VBox([\n",
    "            widgets.HTML(value=\"<h3>Lifetime Risk Calculator</h3>\"),\n",
    "            self.notional_input.text_widget,\n",
    "            self.ticker,\n",
    "            self.date,\n",
    "            self.daily_volatility.text_widget,\n",
    "            self.unwind_speed_as_pct_adv.text_widget,\n",
    "            widgets.HBox([self.calculate_button, self.reset_button]),\n",
    "            self.output\n",
    "        ])\n",
    "\n",
    "    def on_calculate_clicked(self, button):\n",
    "        with self.output:\n",
    "            clear_output()\n",
    "\n",
    "            # Get values from inputs\n",
    "            notional = self.notional_input.get_value()\n",
    "            ticker = self.ticker.value\n",
    "            date = self.date.value\n",
    "            daily_volatility = self.daily_volatility.get_value()\n",
    "            unwind_speed_as_pct_adv = self.unwind_speed_as_pct_adv.get_value()\n",
    "\n",
    "            # Handle optional daily volatility\n",
    "            daily_volatility = \"Optional\" if daily_volatility == 0.0 else daily_volatility\n",
    "\n",
    "            # Calculate the result\n",
    "            result, daily_vol = lifetime_risk_tcm_bps(notional, ticker, date, daily_volatility, unwind_speed_as_pct_adv)\n",
    "\n",
    "            # Display the inputs and result\n",
    "            data = {\n",
    "                'Parameter': ['Notional', 'Ticker', 'Date', 'Daily Volatility', 'Unwind Speed as Pct Adv', 'Result (TCM BPS)'],\n",
    "                'Value': [f\"${notional:,.2f}\", ticker, date, f\"{daily_vol:.4f}\", f\"{unwind_speed_as_pct_adv:.2%}\", f\"{result:.2f}\"]\n",
    "            }\n",
    "\n",
    "            result_df = pd.DataFrame(data)\n",
    "            display(result_df)\n",
    "\n",
    "    def on_reset_clicked(self, b):\n",
    "        self.notional_input.text_widget.value = \"1,000,000\"\n",
    "        self.ticker.value = \"AAPL\"\n",
    "        self.date.value = dt.date(2025, 6, 10)\n",
    "        self.daily_volatility.text_widget.value = \"0.0\"\n",
    "        self.unwind_speed_as_pct_adv.text_widget.value = \"0.03\"\n",
    "\n",
    "        with self.output:\n",
    "            clear_output()\n",
    "            print(\"Values reset to defaults.\")\n",
    "\n",
    "    def display(self):\n",
    "        display(self.container)\n",
    "\n",
    "# Create and display the calculator\n",
    "calculator = LifetimeRiskCalculator()\n",
    "calculator.display()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
